package com.hup.utils.io.audio.sourceDataLine;

import com.hup.utils.io.audio.AudioInfo;
import com.hup.utils.io.audio.AudioPlayTask;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 音频播放任务类;自主代码实现:循环播放,暂停等;
 * 不能调节音量;
 * 这是最原始的实现方式!!!一开始找到的资料就是这种╮(╯_╰)╭
 *
 * @author hugan
 * @date 2020/5/28
 * @apiNote 支持文件和格式:{@link AudioInfo#AudioInfo(String)}
 * @deprecated 不可理喻的坑, 开发n天内都正常的代码, 突然播放时就一直卡顿了, 重启电脑无效, 隔了大半天后又好了, 和系统/硬件相关??CLip不会这样
 */
@Log4j2
@Getter
@Deprecated
public class SourceDataPlayTask implements AudioPlayTask {

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 15,
            60, TimeUnit.SECONDS,
            new SynchronousQueue<>()
    );

    /**
     * 播放缓存大小;太小时(如1024)声音会变成'卡卡的',没深究
     */
    private static final int DEF_BUFFER_SIZE = 1024 * 3;

    private final TaskListener taskListener;
    private final AudioInfo audioInfo;
    /**
     * 缓存大小
     */
    @Setter
    private int bufferSize = DEF_BUFFER_SIZE;
    /**
     * 循环播放
     */
    @Setter
    private boolean loop;

    //内部状态属性.st
    @Getter(AccessLevel.PACKAGE)
    private ReentrantLock lock = new ReentrantLock();

    @Getter(AccessLevel.PACKAGE)
    private Condition condition = lock.newCondition();

    /**
     * 播放中:true-开始播放到播放结束前,包含暂停中
     */
    private boolean playing;
    /**
     * 播放途中调用 {@link #play()},重新播放
     */
    @Setter(AccessLevel.PACKAGE)
    private boolean restarting;

    /**
     * true-暂停中
     */
    @Setter(AccessLevel.PACKAGE)
    private boolean pausing;
    /**
     * 停止中,调用了stop方法后为true
     */
    private boolean stopping;
    /**
     * 开始播放的时间
     */
    private long startTime;
    /**
     * 上次播放耗时
     */
    private long lastPlaySpan;
    //内部状态属性.ed


    /**
     * @param resourcePath {@link AudioInfo#AudioInfo(String)}
     * @param taskListener nullable
     */
    public SourceDataPlayTask(String resourcePath, TaskListener<SourceDataPlayTask> taskListener) {
        this.taskListener = taskListener;
        AudioInfo temp = null;
        try {
            temp = new AudioInfo(resourcePath);
            if (taskListener != null) taskListener.onGetInfo(temp, null, this);
        } catch (Exception e) {
            if (taskListener != null) taskListener.onGetInfo(null, e, this);
        }
        audioInfo = temp;
    }

    private void executePlay() {
        EXECUTOR.execute(() -> {
            try (SourceDataLineBundle bundle = new SourceDataLineBundle(this)) {
                byte[] buff = new byte[getBufferSize()];
                log.info("缓存大小={}", buff.length);
                int readLength;
                while ((readLength = bundle.readFormAudio(buff)) > 0) {
                    bundle.writeToLine(buff, readLength);

                    if (this.isStopping()) break;
                    if (this.isPausing()) {
                        try {
                            lock.lock();
                            log.debug("暂停.st");
                            if (taskListener != null) taskListener.onPausedOrResume(true);
                            condition.await();
                            log.debug("暂停.ed");
                            this.setPausing(false);
                            if (taskListener != null) taskListener.onPausedOrResume(false);
                        } finally {
                            lock.unlock();
                        }
                    }
                }
            } catch (Exception e) {
                if (taskListener != null) taskListener.onPlayException(this, new RuntimeException("播放错误", e));
            } finally {
                afterStopped();
            }
        });
    }

    private void afterStopped() {
        try {
            lock.lock();
            log.debug("播放-结束");
            playing = false;
            pausing = stopping = restarting = false;
            lastPlaySpan = System.currentTimeMillis() - startTime;
            if (taskListener != null) taskListener.onStopped();
            //log.info("模拟回调耗时");
            //ThreadUtil.sleep(3000);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 启动播放;
     * 播放中调用此方法时,从新开始播放
     */
    @Override
    public void play() {
        if (audioInfo == null) return;

        try {
            lock.lock();
            if (!playing) {
                log.debug("播放-开始");
                playing = true;
                pausing = stopping = restarting = false;

                startTime = System.currentTimeMillis();
                if (taskListener != null) taskListener.onStartPlay();
                executePlay();
            } else {
                log.debug("播放-重置");
                restarting = true;
                if (pausing) condition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 从指定地方开始播放
     *
     * @param atPercent 范围[0,1)
     */
    public void playAtPercent(float atPercent) {
        if (audioInfo == null) return;
        if (atPercent < 0 || atPercent >= 1) {
            throw new IllegalArgumentException("比例错误:" + atPercent);
        }
        playAtSecond(audioInfo.getTimeSpan() * atPercent);
    }

    /**
     * 从指定地方开始播放
     *
     * @param atSecond 范围[0, {@link AudioInfo#getTimeSpan()})
     */
    public void playAtSecond(float atSecond) {
        if (audioInfo == null) return;
        if (atSecond < 0 || atSecond >= audioInfo.getTimeSpan()) {
            throw new IllegalArgumentException("比例错误:" + atSecond);
        }
        //未实现,逻辑应该不会很难,在player中重置流并设置起点就好了
        throw new IllegalArgumentException("未实现!");
    }

    /**
     * @apiNote 此方法线程安全不敏感
     */
    @Override
    public void pause(Boolean isPause) {
        if (audioInfo == null || !playing) return;
        try {
            lock.lock();
            if (isPause == null) {
                pausing = !pausing;
            } else {
                pausing = isPause;
            }
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /**
     * @apiNote 此方法线程安全不敏感
     */
    @Override
    public void stop() {
        if (audioInfo == null || !playing) return;
        stopping = true;
        try {
            lock.lock();
            condition.signalAll();
            //唤醒暂停中的线程
        } finally {
            lock.unlock();
        }
    }

}
